package net.geant.gembus.registry_publisher.internal;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import net.geant.gembus.registry_publisher.RegistryPublishService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RegistryPublishServiceImpl implements RegistryPublishService {

	/* Content types */
	public static String XML_CONTENT_TYPE = "xml";
	public static String JSON_CONTENT_TYPE = "json";
	
	String url;
	/**
	 * Logger Object to write Logs
	 */
	private static final Logger logger = LoggerFactory.getLogger(RegistryPublishServiceImpl.class);
	private static Map<String, String> contentTypes = new HashMap<String, String>();
	
	static {
		contentTypes.put(XML_CONTENT_TYPE, "application/rdf+xml");
		contentTypes.put(JSON_CONTENT_TYPE, "application/json");
	}
	
	public RegistryPublishServiceImpl(String url, String serviceName, String domainName) {
		String concat = serviceName + "/" + domainName + "/";
		this.url = url.endsWith("/") ? url + concat : url + "/" + concat;
	}
	
	/**
	 * Publishes a service to the GEMBus Registry by sending a PUT operation to the base URL + serviceId,
	 * with the WSDL/WADL/USDL/any_other_descriptor in the body of the PUT request
	 * @param serviceId
	 * @param descriptor
	 */
	public void publishService(String serviceId, String descriptor) throws Exception {
		String theUrl = url + serviceId;
		HttpURLConnection connection;
		try {
			connection = (HttpURLConnection) new URL(theUrl).openConnection();
			connection.addRequestProperty("accept", contentTypes.get(RegistryPublishServiceImpl.JSON_CONTENT_TYPE));
			connection.setRequestMethod("PUT");
			connection.setDoOutput(true);
			connection.connect();
			OutputStream os = connection.getOutputStream();
			os.write(descriptor.getBytes());
			os.close();
			int statusCode = connection.getResponseCode();
			if (!Integer.toString(statusCode).startsWith("2")) {
				logger.error("Bad status code: " + url);
				throw new Exception("Registry service returned status code: " + statusCode);
			}
			//InputStream is = connection.getInputStream();
			//String res = this.inputStreamToString(is);
			//is.close();
		} catch (Exception e) {
			logger.error("Exception: " + e);
			e.printStackTrace();
			throw e;
		}
		
	}
	
	/**
	 * Removes a service from the GEMBus Registry by sending a HTTP DELETE to the base URL of the registry + serviceId
	 * @param serviceId
	 */
	public void removeService(String serviceId) throws Exception {
		String theUrl = url + serviceId;
		HttpURLConnection connection;
		try {
			connection = (HttpURLConnection) new URL(theUrl).openConnection();
			connection.addRequestProperty("accept", contentTypes.get(RegistryPublishServiceImpl.JSON_CONTENT_TYPE));
			connection.setRequestMethod("DELETE");
			connection.connect();
			connection.getResponseCode();
			int statusCode = connection.getResponseCode();
			if (!Integer.toString(statusCode).startsWith("2")) {
				logger.error("Bad status code: " + url);
				throw new Exception("Registry service returned status code: " + statusCode);
			}
		} catch (Exception e) {
			logger.error("Exception: " + e);
			e.printStackTrace();
			throw e;
		}
	}

	/**
	 * Publishes meaningful metadata about a service to the GEMBus Registry. The metadata is to be provided in the form of a Map<String, String>.
	 * This is meant to publish collateral metadata such as SLAs, Owners, etc. to make it queryable by clients.
	 * In this prototype, it is only used by the Camel integration to publish *all* endpoint properties to the Registry (just as a Proof of Concept).
	 * The operation performed is a POST to the base URL of the registry + "/metadata/" + serviceId (of the service to which the metadata belongs)
	 * The wire format is JSON.
	 * @param serviceId
	 * @param metadata
	 */
	public void updateMetadata(String serviceId, Map<String, String> metadata) throws Exception {
		String theUrl = url + "metadata/" + serviceId;
		HttpURLConnection connection;
		// rudimentary generation of JSON to POST; take all key => value attributes from the metadata map and form a JSON string
		StringBuilder json = new StringBuilder("{ ");
		for (String key : metadata.keySet()) {
			// generate a key-value pair with format "key": "value", add a comma at the end
			json.append("\"" + key + "\": " + "\"" + metadata.get(key) + "\", ");
		}
		// once done, remove the last comma+space
		json.delete(json.length() - 2, json.length());
		// append the closing }
		json.append(" }");
		
		try {
			connection = (HttpURLConnection) new URL(theUrl).openConnection();
			connection.addRequestProperty("Accept", contentTypes.get(RegistryPublishServiceImpl.JSON_CONTENT_TYPE));
			connection.addRequestProperty("Content-Type", contentTypes.get(JSON_CONTENT_TYPE));
			connection.setRequestMethod("POST");
			connection.setDoOutput(true);
			connection.connect();
			OutputStream os = connection.getOutputStream();
			os.write(json.toString().getBytes("utf-8"));
			os.close();
			connection.getResponseCode();
			int statusCode = connection.getResponseCode();
			if (!Integer.toString(statusCode).startsWith("2")) {
				logger.error("Bad status code: " + url);
				throw new Exception("Registry service returned status code: " + statusCode);
			}
		} catch (Exception e) {
			logger.error("Exception: " + e);
			e.printStackTrace();
			throw e;
		}
	}
	
	private String inputStreamToString(InputStream is) {
		Writer writer = new StringWriter();
		char[] buffer = new char[1024];
		
		try {
			Reader reader = new BufferedReader(new InputStreamReader(is, "utf-8"));
			int n;
			while ((n = reader.read(buffer)) != -1) {
				writer.write(buffer);
			}
			
			return writer.toString();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
}
